package com.baby.work.domain.grape;

import java.util.ArrayList;
import java.util.List;

public class NumberHandler {

    private final List<Character> OctNonZeroNumber = new ArrayList<Character>(){{ add('1'); add('2'); add('3');
        add('4'); add('5'); add('6'); add('7'); add('8'); add('9');}};
    private final List<Character> OctNumber = new ArrayList<Character>(){{ addAll(OctNonZeroNumber); add('0');}};
    private final List<Character> legalOctFirstChar = new ArrayList<Character>(){{ addAll(OctNumber); add('-');}};
    private final List<Character> legalOctNonFirstChar = new ArrayList<Character>(){{ addAll(OctNumber); add('e');
        add('E'); add('.'); add('+');}};


    private final List<Character> specialHexNumber = new ArrayList<Character>(){{ add('A'); add('B'); add('C');
        add('D'); add('E'); add('F'); add('a'); add('b'); add('c'); add('d'); add('e'); add('f'); }};
    private final List<Character> hexNumber = new ArrayList<Character>(){{ addAll(OctNumber); addAll(specialHexNumber);}};
    private final List<Character> legalHexFirstChar =  new ArrayList<Character>(){{ addAll(legalOctFirstChar); addAll(specialHexNumber);}};
    private final List<Character> legalHexNonFirstChar =  new ArrayList<Character>(){{ addAll(hexNumber); add('.');}};


    private boolean isContainsSameChar(String input, char ch) {
        return !(input.indexOf(ch) == input.lastIndexOf(ch));
    }

    private boolean isPointLegal(String input, int index, String type) {
        if (input.length() <= index+1) {
            return false;
        }
        if (isContainsSameChar(input, '.')) {
            return false;
        }
        char nextChar = input.charAt(index+1);
        if (!getLegalNumberList(type).contains(nextChar)) {
            return false;
        }
//        if (input.endsWith("0")) {
//            return false;
//        }
        return true;
    }

    private boolean isScientificNotationLegal(String input, int index) {
        if (input.length() <= index+2) {
            return false;
        }
        if (isContainsSameChar(input, input.charAt(index))) {
            return false;
        }
        char nextChar = input.charAt(index+1);
        if (nextChar != '+'){
            return false;
        }
        char afterNextChar = input.charAt(index+2);
        if (!OctNumber.contains(afterNextChar)){
            return false;
        }
        return true;
    }

    private boolean isZeroLegal(String input, boolean isFirstChar, String type) {
        if (isFirstChar) {
            if (input.length() == 1){
                return true;
            }

            if (input.startsWith("0.")) {
                if (isPointLegal(input, 1, type)){
                    return true;
                }
            }

            if (input.startsWith("-0.")) {
                if (isPointLegal(input, 2, type)){
                    return true;
                }
            }
            return false;
        }

        return true;
    }

    private List<Character> getLegalFirstChar(String type) {
        if (type.equals("Oct")) {
            return legalOctFirstChar;
        }

        if (type.equals("Hex")) {
            return legalHexFirstChar;
        }

        return new ArrayList<>();
    }

    private List<Character> getLegalNumberList(String type) {
        if (type.equals("Oct")) {
            return OctNumber;
        }

        if (type.equals("Hex")) {
            return hexNumber;
        }

        return new ArrayList<>();
    }

    private boolean isNumberLegalByFirstChar(String input, String type) {
        char firstChar = input.charAt(0);
        if (!getLegalFirstChar(type).contains(firstChar)) {
            return false;
        }
        if (firstChar == '0') {
            return isZeroLegal(input, true, type);
        }
        if (firstChar == '-') {
            if (input.length() <= 1) {
                return false;
            }
            String remainStr = input.substring(1);
            if (remainStr.indexOf('-') > 0) {
                System.out.println("include multi - ");
                return false;
            }
            char nextChar = input.charAt(1);
            if (nextChar == '0') {
                return isZeroLegal(input, true, type);
            }
            return getLegalNumberList(type).contains(nextChar);
        }
        return true;
    }


    public boolean isOct(String input) {

        // Note: 1.0 0.0 2.0e+9 均为不合法

        if (input.length() <= 0) {
            return false;
        }

        if (!isNumberLegalByFirstChar(input, "Oct")) {
            return false;
        }

        if (input.length() == 1) {
            return true;
        }

        for (int i = 1; i < input.length(); i++) {
            char currentChar = input.charAt(i);

            if (!legalOctNonFirstChar.contains(currentChar)) {
                return false;
            }

            if (currentChar == '.' && !isPointLegal(input, i, "Oct")) {
                return false;
            }

            if (currentChar == 'e' || currentChar == 'E') {
                if (!isScientificNotationLegal(input, i)){
                    return false;
                }
            }
        }

        return true;
    }

    public boolean isHex(String input){
        if (input.length() <= 0) {
            return false;
        }

        if (!isNumberLegalByFirstChar(input, "Hex")) {
            return false;
        }

        if (input.length() == 1) {
            return true;
        }

        for (int i = 1; i < input.length(); i++) {
            char currentChar = input.charAt(i);

            if (!legalHexNonFirstChar.contains(currentChar)) {
                return false;
            }

            if (currentChar == '.' && !isPointLegal(input, i, "Hex")) {
                return false;
            }
        }

        return true;
    }

    public static void main(String[] args) {

        NumberHandler numberHandler = new NumberHandler();

        List<String> dataSets = new ArrayList<String>(){{
            add("001");
            add("010");
            add("0");
            add("000");
            add("101");
            add("100");
            add("0.1");
            add("0.10");
            add("0.100");
            add("0.0001");
            add("0..49");
            add("0.e9");
            add("1.0e");
            add("1.0e+0");
            add("0.1e+4");
            add("83e+e");
            add("10.00e+3");
            add("0.9e+");
            add("a.A");
            add("0.a");
            add("B.0");}};

        for (String item : dataSets) {
            System.out.println("item: " + item + "  Oct: " + numberHandler.isOct(item) + "   Hex: " + numberHandler.isHex(item));
        }
    }
}
